package com.lsh.oms.core.service.order;

import com.alibaba.fastjson.JSON;
import com.lsh.oms.core.dao.order.*;
import com.lsh.oms.core.enums.*;
import com.lsh.oms.core.exception.OperateTimeOutException;
import com.lsh.oms.core.model.order.*;
import com.lsh.oms.core.model.po.OmsTask;
import com.lsh.oms.core.service.po.OmsTaskService;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * 创建订单service
 *
 * @author huangdong
 * @date 16/8/4
 */
@Service
public class OrderCreateService {

    private final Logger logger = Logger.getLogger(this.getClass());

    @Autowired
    private OrderHeadDao orderHeadDao;

    @Autowired
    private OrderDetailDao orderDetailDao;

    @Autowired
    private OrderHeadExtDao orderHeadExtDao;

    @Autowired
    private OrderOperateLogDao orderOperateLogDao;

    @Autowired
    private OrderLockDao orderLockDao;

    @Autowired
    private OmsTaskService omsTaskService;


    /**
     * 创建订单
     *
     * @param order
     * @param poOrderFlag
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Long create(OrderHead order, String poOrderFlag) {
        long insertDbTsS = System.currentTimeMillis();
        StringBuilder logSign = new StringBuilder("创建订单_").append(order.getMisOrderNo()).append("_").append(order.getOrderCode()).append("：");
        int logSignLen = logSign.length();

        Long orderCode = order.getOrderCode();
        long lockTsS = System.currentTimeMillis();
        long lockCost;
        OrderLock lock = new OrderLock();
        lock.setCode(order.getAddressCode());
        lock.setType(YN.Y.getValue());
        lock.setCreateTime(order.getCreateTime());
        lock.setUpdateTime(order.getCreateTime());
        lock.setVenderId(order.getVenderId());
        try {
            this.orderLockDao.insert(lock);
        } catch (DuplicateKeyException e) {
            throw new OperateTimeOutException("1");
        } catch (Throwable e) {
            throw new OperateTimeOutException("2");
        } finally {
            lockCost = System.currentTimeMillis() - lockTsS;
            logSign.setLength(logSignLen);
            logger.info(logSign.append("lock address耗时：").append(lockCost).append("ms... address_code=").append(order.getAddressCode()).toString());
        }

//        boolean first = true;
//        if (order.getOrderStatus() != OrderStatus.ORDER_NO_EDIT.getIndex() && order.getRegionCode() != 2000 && order.getRegionCode() != 8000) {
//            first = orderHeadDao.countByAddressCode(order.getAddressCode(), OMSUtils.currentDate()) <= 0;
//            logSign.setLength(logSignLen);
//            logger.info(logSign.append("是否是今日首单: ").append(first).toString());
//        }

        //如果订单状态不是`未审核`，并且订单是客户今日首单，将订单状态置为`未审核`
//        long firstCQCost = 0;
        if (poOrderFlag.equals(PoOrderFlag.PO_NO_ORDER.getType())) {
            if (!(order.getPayType().equals(PayType.PAY_ON_DELIVERY.getValue()))) {

                BigDecimal expectPayAmount = order.getExpectPayAmount();
                if (expectPayAmount.compareTo(BigDecimal.ZERO) == 0) {
                    order.setOrderStatus(OrderStatus.ORDER_SPLIT_CHECK.getIndex());
                    order.setPayStatus(2);
                } else {
                    order.setOrderStatus(OrderStatus.ORDER_NEW.getIndex());
                }
            } else if (order.getOrderStatus() != OrderStatus.ORDER_NO_EDIT.getIndex()) {
//                if (!first) {
//                    order.setOrderStatus(OrderStatus.ORDER_NO_EDIT.getIndex());
//                }
            }
        } else if (poOrderFlag.equals(PoOrderFlag.CONFIRM_ORDER.getType())) {
            if (!(order.getPayType().equals(PayType.PAY_ON_DELIVERY.getValue()))) {

                BigDecimal expectPayAmount = order.getExpectPayAmount();
                if (expectPayAmount.compareTo(BigDecimal.ZERO) == 0) {
                    order.setOrderStatus(OrderStatus.ORDER_SPLIT_CHECK.getIndex());
                    order.setPayStatus(2);
                } else {
                    order.setOrderStatus(OrderStatus.ORDER_NEW.getIndex());
                }
            }
        } else if (poOrderFlag.equals(PoOrderFlag.PRE_ORDER.getType())) {
            order.setOrderStatus(OrderStatus.ORDER_PRE.getIndex());
        }

        //插入订单头信息
        long insertHeadTsS = System.currentTimeMillis();
        logger.info("插入父单 " + JSON.toJSONString(order));
        this.orderHeadDao.insert(order);
        long insertHeadCost = System.currentTimeMillis() - insertHeadTsS;

        long insertHeadExtTsS = System.currentTimeMillis();
        order.getExtObject().setId(order.getId());
        this.orderHeadExtDao.insert(order.getExtObject());
        long insertHeadExtCost = System.currentTimeMillis() - insertHeadExtTsS;
        logSign.setLength(logSignLen);
        logger.info(logSign.append("插入订单头信息耗时：").append(insertHeadCost).append("ms\t").append(insertHeadExtCost).append("ms").toString());

        //插入订单明细
        long insertDetailTsS = System.currentTimeMillis();
        this.orderDetailDao.insertBatch(order.getDetails());
        long insertDetailCost = System.currentTimeMillis() - insertDetailTsS;
        logSign.setLength(logSignLen);
        logger.info(logSign.append("插入订单明细耗时：").append(insertDetailCost).append("ms...共").append(order.getDetails().size()).append("条").toString());

        //插入订单操作日志
        long insertLogTsS = System.currentTimeMillis();
        OrderOperateLog orderLog = new OrderOperateLog();
        orderLog.setOrderCode(orderCode);
        orderLog.setOrderType(OperateOrderType.ORDER.getValue());
        orderLog.setOperateType(OrderOperateType.ORDER_NEW.getValue());
        orderLog.setOperateDesc(new StringBuilder(OrderOperateType.ORDER_NEW.getDesc()).append("，订单状态为`").append(OrderStatus.getName(order.getOrderStatus())).append("`").toString());
        orderLog.setOperateTime(order.getCreateTime());
        orderLog.setClientIp(order.getExtObject().getIp());
        orderLog.setClientType(order.getExtObject().getFromType());
        orderLog.setCreateTime(order.getCreateTime());
        orderLog.setVenderId(order.getVenderId());
        this.orderOperateLogDao.insert(orderLog);
        long insertLogCost = System.currentTimeMillis() - insertLogTsS;
        logSign.setLength(logSignLen);
        logger.info(logSign.append("插入订单操作日志耗时：").append(insertLogCost).append("ms").toString());

        if (order.getOrderStatus() == OrderStatus.ORDER_SPLIT_CHECK.getIndex()) {
            this.addTask(orderCode + "", order.getVenderId());
        }

        long unlockTsS = System.currentTimeMillis();
        long unlockCost;
        try {
            orderLockDao.deleteByFilter(lock);
        } catch (Throwable e) {
            throw new OperateTimeOutException("3");
        } finally {
            unlockCost = System.currentTimeMillis() - unlockTsS;
            logSign.setLength(logSignLen);
            logger.info(logSign.append("unlock address耗时：").append(unlockCost).append("ms... address_code=").append(order.getAddressCode()).toString());
        }

        return order.getOrderCode();
    }

    /**
     * 拆单任务添加
     *
     * @param orderCode
     */
    public void addTask(String orderCode, Long venderId) {
        Integer timestamp = (int) (System.currentTimeMillis() / 1000);

        OmsTask omsTask = new OmsTask();
        omsTask.setRefCode(orderCode);
        omsTask.setContent(orderCode);
        omsTask.setTaskStatus(TaskStatus.TASK_UN_DEAL.getCode());
        omsTask.setTaskType(TaskType.ORDER_SPLIT.getCode());
        omsTask.setExecCount(0);
        omsTask.setCreateTime(timestamp);
        omsTask.setUpdateTime(timestamp);
        omsTask.setVenderId(venderId);

        omsTaskService.add(omsTask);
    }


    private OrderLock orderLock(Long addressCode, StringBuilder logSign, Long venderId) {

        Integer timestamp = (int) (System.currentTimeMillis() / 1000);

        OrderLock lock = new OrderLock();
        lock.setCode(addressCode);
        lock.setType(YN.Y.getValue());
        lock.setCreateTime(timestamp);
        lock.setUpdateTime(timestamp);
        lock.setVenderId(venderId);

        long lockTsS = System.currentTimeMillis();
        long lockCost;
        try {
            this.orderLockDao.insert(lock);
        } catch (DuplicateKeyException e) {
            throw e;
        } catch (Throwable e) {
            throw new RuntimeException("order lock Exception!!", e);
        } finally {
            lockCost = System.currentTimeMillis() - lockTsS;
            logger.info(logSign.append("lock address耗时：").append(lockCost).append("ms... address_code=").append(addressCode).toString());
        }

        return lock;
    }

    /**
     * @param orderHeads
     * @param logPrefix
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public List<String> createSplitList(OrderHead parentOrderHead, Set<OrderHead> orderHeads, String logPrefix, Long venderId) {

        if (CollectionUtils.isEmpty(orderHeads) || orderHeads.size() < 2) {
            return Collections.EMPTY_LIST;
        }

        OrderHead update = new OrderHead();
        update.setValid(0);
        OrderHead filter = new OrderHead();
        filter.setOrderCode(parentOrderHead.getOrderCode());
        orderHeadDao.updateByFilter(update, filter);

        long insertDbTsS = System.currentTimeMillis();
        StringBuilder logSign = new StringBuilder(logPrefix);
        int logSignLen = logSign.length();

        List<String> misAndOrderCodeList = new ArrayList<>(orderHeads.size());
        Long addressCode = orderHeads.iterator().next().getAddressCode();

        // 订单锁
        OrderLock lock = this.orderLock(addressCode, logSign, venderId);

        List<OrderHeadExt> orderHeadExts = new ArrayList<>(orderHeads.size());
        List<OrderDetail> orderDetails = new ArrayList<>();
        List<OrderOperateLog> orderOperateLogs = new ArrayList<>(orderHeads.size());

        int orderDetailSize = 0;
        for (OrderHead order : orderHeads) {
            //
            orderDetailSize += order.getDetails().size();

            Long orderCode = order.getOrderCode();
            // 操作日志
            OrderOperateLog orderLog = new OrderOperateLog();
            orderLog.setOrderCode(orderCode);
            orderLog.setOrderType(OperateOrderType.ORDER.getValue());
            orderLog.setOperateType(OrderOperateType.ORDER_NEW.getValue());
            orderLog.setOperateDesc(new StringBuilder(OrderOperateType.ORDER_NEW.getDesc()).append("，订单状态为`").append(OrderStatus.getName(order.getOrderStatus())).append("`").toString());
            orderLog.setOperateTime(order.getCreateTime());
            orderLog.setClientIp(order.getExtObject().getIp());
            orderLog.setClientType(order.getExtObject().getFromType());
            orderLog.setCreateTime(order.getCreateTime());
            orderLog.setVenderId(venderId);
            orderOperateLogs.add(orderLog);

            orderDetails.addAll(order.getDetails());

            Map<String, String> orderCodes = new HashMap<>((int) (orderHeads.size() / 0.75) + 1);
            orderCodes.put("mis_order_id", order.getMisOrderNo());
            orderCodes.put("order_id", order.getOrderCode().toString());
            orderCodes.put("is_valid", order.getValid().toString());

            misAndOrderCodeList.add(JSON.toJSONString(orderCodes));
        }

        //插入订单头信息
        for (OrderHead orderHead : orderHeads) {
            // 插入数据库,并返回主键
            logger.info("新建订单开始 " + JSON.toJSONString(orderHead));
            this.orderHeadDao.insert(orderHead);
            orderHead.getExtObject().setId(orderHead.getId());
            orderHeadExts.add(orderHead.getExtObject());
        }

        long insertHeadExtTsS = System.currentTimeMillis();
        this.orderHeadExtDao.insertBatch(orderHeadExts);
        long insertHeadExtCost = System.currentTimeMillis() - insertHeadExtTsS;

        this.orderDetailDao.insertBatch(orderDetails);

        this.orderOperateLogDao.insertBatch(orderOperateLogs);

        try {
            orderLockDao.deleteByFilter(lock);
        } catch (Throwable e) {
            throw new RuntimeException("order unlock Exception!!", e);
        }
        long insertDbTsE = System.currentTimeMillis();
        logSign.setLength(logSignLen);
        logger.info(logSign.append("创建订单插入数据库耗时：").append(insertDbTsE - insertDbTsS).append("ms")
                .append("\t").append(orderDetailSize)
                .append("\t").append(insertHeadExtCost)
                .toString());

        return misAndOrderCodeList;
    }


    @Transactional
    public Map<Long, Integer> enable(List<Long> codes) {

        Map<Long, Integer> map = new HashMap<>();

        if (CollectionUtils.isEmpty(codes)) {
            return map;
        }
        for (Long code : codes) {
            OrderHead filter = new OrderHead();
            filter.setOrderCode(code);
            if (this.orderHeadDao.count(filter) <= 0) {
//                throw new BusinessException("E101", "order is not exist! code=" + code);
            }
            filter.setValid(YN.N.getValue());
            OrderHead entity = new OrderHead();
            entity.setValid(YN.Y.getValue());
            map.put(code, this.orderHeadDao.updateByFilter(entity, filter));
        }

        return map;
    }

}
